#include "ArvoreSintatica.h"

#include <stdlib.h>

ArvoreSintatica::ArvoreSintatica(std::queue<Tokem> *saidaLexico) {
	fila = saidaLexico;

	raiz = new Nodo(PROGRAMA);
	ERROS erro = programa(raiz);
	if (erro != SEM_ERROS ) {
		this->erro = erro;
	}
}

ArvoreSintatica::~ArvoreSintatica() {
	destroiArvore(raiz);
}

ERROS ArvoreSintatica::destroiArvore(Nodo *raiz) {
	if (raiz->filhos->empty() ) {
		delete raiz;
	} else {
		while (!raiz->filhos->empty()) {
			destroiArvore(&raiz->filhos->front());
			raiz->filhos->pop_front();
		}
	}
	return SEM_ERROS;
}

ERROS ArvoreSintatica::programa(Nodo *raiz) {
	Nodo *novoNoh;

	// program
	if (fila->back().tipo == PAL_RESERVADA && fila->back().valorlido == PROGRAM ) {

		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
	} else {
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), PROGRAM, TOKEN_INVALIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	// identificador
	if (fila->front().tipo == IDENTIFICADOR ) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
	} else {
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	// (
	if (fila->front().tipo == SIMBOLO && fila->front().valorlido == ABRE_PARENTESES ) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
	} else {
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), ABRE_PARENTESES , TOKEN_INVALIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	while (true) {
		// identificador
		if (fila->front().tipo == IDENTIFICADOR ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}
		// )
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == FECHA_PARENTESES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
			break;
		}
		// ,
		else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}
	} //FIM WHILE

	// ;
	if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
	} else {
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	// BLOCO
	raiz->filhos->push_back(*(new Nodo(BLOCO)));
	ERROS erro = bloco(&raiz->filhos->back());
	if (erro != SEM_ERROS ) {
		return erro;
	}

	// .
	if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
	} else {
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), PONTO , TOKEN_INVALIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	return SEM_ERROS;
}

ERROS ArvoreSintatica::bloco(Nodo *raiz) {
	Nodo *novoNoh;
	ERROS erro;

	// label
	if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == LABEL) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// numero
		do {
			if (fila->front().tipo == INTEIRO) {
				novoNoh = new Nodo(&fila->front());
				raiz->filhos->push_back(*novoNoh);
				fila->pop();
			} else {
				novoNoh = new Nodo();
				novoNoh->Erro(&fila->front(), INTEIRO , TOKEN_INVALIDO);
				raiz->filhos->push_back(*novoNoh);
				return TOKEN_INVALIDO;
			}
			// virgula
		} while (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA);

		// ponto e virgula
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}
		// var
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == VAR) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		do {
			// se virgula, consumi-la
			if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA) {
				fila->pop();
			}

			// identificador
			if (fila->front().tipo == IDENTIFICADOR) {
				novoNoh = new Nodo(&fila->front());
				raiz->filhos->push_back(*novoNoh);
				fila->pop();
			} else {
				novoNoh = new Nodo();
				novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
				raiz->filhos->push_back(*novoNoh);
				return TOKEN_INVALIDO;
			}

			// =
			if (fila->front().tipo == SIMBOLO && fila->front().valorlido == IGUAL) {
				novoNoh = new Nodo(&fila->front());
				raiz->filhos->push_back(*novoNoh);
				fila->pop();
				break;
			}
			//virgula
		} while (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA);

		// tipo
		if (fila->front().tipo == IDENTIFICADOR) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// ;
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// procedure
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == PROCEDURE) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// identificador
		if (fila->front().tipo == IDENTIFICADOR) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// parametros formais
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido== ABRE_PARENTESES ) {
			raiz->filhos->push_back(*(new Nodo(PARAMETROS_FORMAIS)));
			erro = parametros_formais(&raiz->filhos->back());
			if (erro != SEM_ERROS) {
				this->erro = erro;
				return erro;
			}
		}

		// ;
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// bloco
		raiz->filhos->push_back(*(new Nodo(BLOCO)));
		erro = bloco(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// ;
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// function
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == FUNCTION) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// identificador
		if (fila->front().tipo == IDENTIFICADOR) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// parametros formais
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido== ABRE_PARENTESES ) {
			raiz->filhos->push_back(*(new Nodo(PARAMETROS_FORMAIS)));
			erro = parametros_formais(&raiz->filhos->back());
			if (erro != SEM_ERROS) {
				this->erro = erro;
				return erro;
			}
		}

		// :
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == DOIS_PONTOS ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// identificador
		if (fila->front().tipo == IDENTIFICADOR) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// ;
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// bloco
		raiz->filhos->push_back(*(new Nodo(BLOCO)));
		erro = bloco(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// ;
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

	}
	// begin
	else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == BEGIN) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		do {
			raiz->filhos->push_back(*(new Nodo(COMANDO)));
			erro = comando(&raiz->filhos->back());
			if (erro != SEM_ERROS) {
				this->erro = erro;
				return erro;
			}
		} while (fila->front().valorlido != END );
		fila->pop();

	} else {
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), DESCONHECIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	return SEM_ERROS;
}

ERROS ArvoreSintatica::parametros_formais(Nodo *raiz) {
	Nodo *novoNoh;

	// consome (
	fila->pop();
	do {
		// var
		if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == VAR ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

		}

		// identificador
		if (fila->front().tipo == IDENTIFICADOR) {

			do {
				if (fila->front().tipo == IDENTIFICADOR) {

					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();
				} else {
					novoNoh = new Nodo();
					novoNoh->Erro(&fila->front(), IDENTIFICADOR ,
							TOKEN_INVALIDO);
					raiz->filhos->push_back(*novoNoh);
					return TOKEN_INVALIDO;
				}

				if (fila->front().valorlido != DOIS_PONTOS ) {
					// ,
					if (fila->front().tipo == SIMBOLO && fila->front().valorlido == (int)VIRGULA) {
						novoNoh = new Nodo(&fila->front());
						raiz->filhos->push_back(*novoNoh);
						fila->pop();
					} else {
						novoNoh = new Nodo();
						novoNoh->Erro(&fila->front(), VIRGULA , TOKEN_INVALIDO);
						raiz->filhos->push_back(*novoNoh);
						return TOKEN_INVALIDO;
					}
				}
				// :
			} while (fila->front().valorlido != DOIS_PONTOS );
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// identificador
			if (fila->front().tipo == IDENTIFICADOR) {

				novoNoh = new Nodo(&fila->front());
				raiz->filhos->push_back(*novoNoh);
				fila->pop();
			} else {
				novoNoh = new Nodo();
				novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
				raiz->filhos->push_back(*novoNoh);
				return TOKEN_INVALIDO;
			}

			// function
		} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == FUNCTION ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			do {
				if (fila->front().tipo == IDENTIFICADOR) {

					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();
				} else {
					novoNoh = new Nodo();
					novoNoh->Erro(&fila->front(), IDENTIFICADOR ,
							TOKEN_INVALIDO);
					raiz->filhos->push_back(*novoNoh);
					return TOKEN_INVALIDO;
				}

				if (fila->front().valorlido != DOIS_PONTOS ) {
					// ,
					if (fila->front().tipo == SIMBOLO && fila->front().valorlido == (int)VIRGULA) {
						novoNoh = new Nodo(&fila->front());
						raiz->filhos->push_back(*novoNoh);
						fila->pop();
					} else {
						novoNoh = new Nodo();
						novoNoh->Erro(&fila->front(), VIRGULA , TOKEN_INVALIDO);
						raiz->filhos->push_back(*novoNoh);
						return TOKEN_INVALIDO;
					}
				}
				// :
			} while (fila->front().valorlido != DOIS_PONTOS );
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// identificador
			if (fila->front().tipo == IDENTIFICADOR) {

				novoNoh = new Nodo(&fila->front());
				raiz->filhos->push_back(*novoNoh);
				fila->pop();
			} else {
				novoNoh = new Nodo();
				novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
				raiz->filhos->push_back(*novoNoh);
				return TOKEN_INVALIDO;
			}

			// procedure
		} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == PROCEDURE ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			do {
				// ,
				if (fila->front().valorlido == VIRGULA ) {
					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();
				}
				// identificador
				if (fila->front().tipo == IDENTIFICADOR) {

					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();
				} else {
					novoNoh = new Nodo();
					novoNoh->Erro(&fila->front(), IDENTIFICADOR ,
							TOKEN_INVALIDO);
					raiz->filhos->push_back(*novoNoh);
					return TOKEN_INVALIDO;
				}
				// ,
			} while (fila->front().valorlido == VIRGULA );

		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), DESCONHECIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// ;
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), PONTO_VIRGULA , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

	} while (fila->front().valorlido != FECHA_PARENTESES );
	novoNoh = new Nodo();
	raiz->filhos->push_back(*novoNoh);
	fila->pop();

	return SEM_ERROS;
}
ERROS ArvoreSintatica::comando(Nodo *raiz) {
	Nodo *novoNoh;
	// identificador
	if (fila->front().tipo == IDENTIFICADOR) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
		// (
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == ABRE_PARENTESES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			do {
				raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
				erro = expressao(&raiz->filhos->back());
				if (erro != SEM_ERROS ) {
					this->erro = erro;
					return erro;
				}
				// ,
				if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA) {
					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();

				}
				// )
			} while (fila->front().valorlido != FECHA_PARENTESES);
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// [
		} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == ABRE_COLCHETES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			do {
				raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
				erro = expressao(&raiz->filhos->back());
				if (erro != SEM_ERROS ) {
					this->erro = erro;
					return erro;
				}
				// ,
				if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA) {
					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();

				}
				// ]
			} while (fila->front().valorlido != FECHA_COLCHETES);
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// :=
		} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == RECEBE) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

		} else {
			//erro
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), RECEBE , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}
		raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
		erro = expressao(&raiz->filhos->back());
		if (erro != SEM_ERROS ) {
			this->erro = erro;
			return erro;
		}
		// begin
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == BEGIN) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		do {
			// comando
			raiz->filhos->push_back(*(new Nodo(COMANDO)));
			erro = comando(&raiz->filhos->back());
			if (erro != SEM_ERROS ) {
				this->erro = erro;
				return erro;
			}
			// ;
			if (fila->front().tipo == SIMBOLO && fila->front().valorlido == PONTO_VIRGULA) {
				novoNoh = new Nodo(&fila->front());
				raiz->filhos->push_back(*novoNoh);
				fila->pop();
			}
			// end
		} while (fila->front().valorlido != END);
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// if
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == IF) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// expressao
		raiz->filhos->push_back(*(new Nodo(COMANDO)));
		erro = comando(&raiz->filhos->back());
		if (erro != SEM_ERROS ) {
			this->erro = erro;
			return erro;
		}

		// then
		if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == THEN) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			//erro
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), RECEBE , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// comando
		raiz->filhos->push_back(*(new Nodo(COMANDO)));
		erro = comando(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// else
		if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == ELSE) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// comando
			raiz->filhos->push_back(*(new Nodo(COMANDO)));
			erro = comando(&raiz->filhos->back());
			if (erro != SEM_ERROS) {
				this->erro = erro;
				return erro;
			}
		}

		// while
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == WHILE) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
		erro = expressao(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// do
		if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == DO) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// comando
			raiz->filhos->push_back(*(new Nodo(COMANDO)));
			erro = comando(&raiz->filhos->back());
			if (erro != SEM_ERROS) {
				this->erro = erro;
				return erro;
			}
		}

		// read
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == READ) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// identificador
		if (fila->front().valorlido == IDENTIFICADOR ) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		} else {
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), IDENTIFICADOR , TOKEN_INVALIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// write
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == WRITE) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// expressao
		raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
		erro = expressao(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

	} else {
		// erro
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), DESCONHECIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	return SEM_ERROS;
}
ERROS ArvoreSintatica::expressao(Nodo *raiz) {
	Nodo *novoNoh;
	ERROS erro;

	// expressao simples
	raiz->filhos->push_back(*(new Nodo(EXPRESSAO_SIMPLES)));
	erro = expressao_simples(&raiz->filhos->back());
	if (erro != SEM_ERROS) {
		this->erro = erro;
		return erro;
	}

	// =
	if (fila->front().tipo == SIMBOLO && fila->front().valorlido == IGUAL) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
		// <>
	} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == IGUAL) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// <
	} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MENOR) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		/*
		 // <= (NAO PRECISA IMPLEMENTAR ? NAO TEM NO LEXICO !)
		 } else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MENOR_IGUAL) {
		 // >=
		 } else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MAIOR_) {
		 */
		// >
	} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == IGUAL) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

	} else {
		// erro
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), DESCONHECIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	// expressao simples
	raiz->filhos->push_back(*(new Nodo(EXPRESSAO_SIMPLES)));
	erro = expressao_simples(&raiz->filhos->back());
	if (erro != SEM_ERROS) {
		this->erro = erro;
		return erro;
	}

	return SEM_ERROS;
}
ERROS ArvoreSintatica::expressao_simples(Nodo *raiz) {
	Nodo *novoNoh;
	ERROS erro;
	// +
	if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MAIS) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// -
	} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MENOS) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();
	}
	// termo
	raiz->filhos->push_back(*(new Nodo(TERMO)));
	erro = termo(&raiz->filhos->back());
	if (erro != SEM_ERROS) {
		this->erro = erro;
		return erro;
	}

	do {
		// +
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MAIS) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// -
		} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == MENOS) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			//or
		} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == OR) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		}

		// termo
		raiz->filhos->push_back(*(new Nodo(TERMO)));
		erro = termo(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

	} while (fila->front().valorlido == MAIS || fila->front().valorlido == MENOS);

	return SEM_ERROS;
}
ERROS ArvoreSintatica::termo(Nodo *raiz) {
	Nodo *novoNoh;
	ERROS erro;

	do {

		//fator
		raiz->filhos->push_back(*(new Nodo(FATOR)));
		erro = fator(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// *
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VEZES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// div
		} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == DIV) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

			// and
		} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == AND) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		}

	} while (fila->front().valorlido == VEZES ||fila->front().valorlido == DIV || fila->front().valorlido == AND);

	return SEM_ERROS;
}
ERROS ArvoreSintatica::fator(Nodo *raiz) {
	Nodo *novoNoh;
	ERROS erro;

	// numero
	if (fila->front().tipo == INTEIRO) {

		// identificador
	} else if (fila->front().tipo == IDENTIFICADOR) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == ABRE_COLCHETES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
			do {
				raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
				erro = expressao(&raiz->filhos->back());
				if (erro != SEM_ERROS) {
					this->erro = erro;
					return erro;
				}

				if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA) {
					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();
				}

			} while (fila->front().valorlido != FECHA_COLCHETES);
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		}
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == ABRE_PARENTESES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
			do {
				raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
				erro = expressao(&raiz->filhos->back());
				if (erro != SEM_ERROS) {
					this->erro = erro;
					return erro;
				}

				if (fila->front().tipo == SIMBOLO && fila->front().valorlido == VIRGULA) {
					novoNoh = new Nodo(&fila->front());
					raiz->filhos->push_back(*novoNoh);
					fila->pop();
				}

			} while (fila->front().valorlido != FECHA_PARENTESES);
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();
		}

		// (
	} else if (fila->front().tipo == SIMBOLO && fila->front().valorlido == ABRE_PARENTESES) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// expressao
		raiz->filhos->push_back(*(new Nodo(EXPRESSAO)));
		erro = expressao(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// )
		if (fila->front().tipo == SIMBOLO && fila->front().valorlido == FECHA_PARENTESES) {
			novoNoh = new Nodo(&fila->front());
			raiz->filhos->push_back(*novoNoh);
			fila->pop();

		} else {
			// erro
			novoNoh = new Nodo();
			novoNoh->Erro(&fila->front(), DESCONHECIDO);
			raiz->filhos->push_back(*novoNoh);
			return TOKEN_INVALIDO;
		}

		// not
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == NOT) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// fator
		raiz->filhos->push_back(*(new Nodo(FATOR)));
		erro = fator(&raiz->filhos->back());
		if (erro != SEM_ERROS) {
			this->erro = erro;
			return erro;
		}

		// true
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == TRUE) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// false
	} else if (fila->front().tipo == PAL_RESERVADA && fila->front().valorlido == FALSE) {
		novoNoh = new Nodo(&fila->front());
		raiz->filhos->push_back(*novoNoh);
		fila->pop();

		// erro
	} else {
		// erro
		novoNoh = new Nodo();
		novoNoh->Erro(&fila->front(), DESCONHECIDO);
		raiz->filhos->push_back(*novoNoh);
		return TOKEN_INVALIDO;
	}

	return SEM_ERROS;
}

void ArvoreSintatica::imprime() {
	this->volta = false;
	this->count = 0;
	percorre(raiz);
}

void ArvoreSintatica::percorre(Nodo *raiz) {

	if (this->volta) {
		return;
	}
	this->count++;
	if (!raiz->isTerminal) {
		naoTerminal(raiz);

	} else {
		terminal(raiz);
	}
}

void ArvoreSintatica::naoTerminal(Nodo *raiz) {

	for (int i = 0; i <= count; i++) {
		printf("\t");
	}
	if (raiz->erro != SEM_ERROS) {
		printf("Erro sintático em: ");
		this->volta = true;
	}

	switch (raiz->nTerminal) {
	case PROGRAMA:
		printf("programa\n");
		break;
	case BLOCO:
		printf("bloco\n");
		break;
	case TIPO:
		printf("tipo\n");
		break;
	case PARAMETROS_FORMAIS:
		printf("parametros formais\n");
		break;
	case COMANDO:
		printf("comando\n");
		break;
	case EXPRESSAO:
		printf("expressao\n");
		break;
	case EXPRESSAO_SIMPLES:
		printf("expressao simples\n");
		break;
	case TERMO:
		printf("termo\n");
		break;
	case FATOR:
		printf("fator\n");
		break;
	};
	if (this->volta) {
		return;
	}
	percorre(&raiz->filhos->front());
}

void ArvoreSintatica::terminal(Nodo *raiz) {
	//Simbolos para impressao
	static const char *simbolos[] = { "-", ".", ",", ";", "(", ")", ":", "=",
			"<>", "<", ">", "+", "*", "[", "]", ":=", ".." };

	//Palavras reservadas para impressao
	static const char *reservadas[] = { "false", "true", "program", "begin",
			"end", "label", "type", "array", "of", "var", "procedure",
			"function", "if", "then", "else", "while", "do", "or", "and",
			"div", "not", "case", "record", "uses", "integer" };

	std::list<Nodo>::iterator it;
	for (it = raiz->filhos->begin(); it != raiz->filhos->end(); it++) {
		if (it->isTerminal) {
			switch (it->token->tipo) {
			case INTEIRO:
				if (it->erro != SEM_ERROS) {
					printf("erro simtatico, l %d, c %d:", it->token->linha,
							it->token->coluna);
					printf("- %d", it->token->valorlido);
					printf("- %d\n", it->token->valoresperado);
					this->volta = true;
					return;
				} else {
					printf("- %d", it->token->valorlido);
				}
				break;
			case IDENTIFICADOR:
				if (it->erro != SEM_ERROS) {
					printf("erro simtatico, l %d, c %d:", it->token->linha,
							it->token->coluna);
					printf("- %s", it->token->nome);
					this->volta = true;
					return;
				} else {
					printf("- %s", it->token->nome);
				}
				break;
			case PAL_RESERVADA:
				if (it->erro != SEM_ERROS) {
					printf("erro simtatico, l %d, c %d:", it->token->linha,
							it->token->coluna);
					printf("valor lido - %s", reservadas[it->token->valorlido]);
					printf("\tvalor esperado - %s\n",
							reservadas[it->token->valoresperado]);
					this->volta = true;
					return;
				} else {
					printf("- %s", reservadas[it->token->valorlido]);
				}
				break;
			case SIMBOLO:
				if (it->erro != SEM_ERROS) {
					printf("erro simtatico, l %d, c %d:", it->token->linha,
							it->token->coluna);
					printf("valor lido - %s", simbolos[it->token->valorlido]);
					printf("\tvalor esperado - %s\n",
							simbolos[it->token->valoresperado]);
					this->volta = true;
					return;
				} else {
					printf("- %s", simbolos[it->token->valorlido]);
				}
				break;
			case SIMBOLO_COMP:
				if (it->erro != SEM_ERROS) {
					printf("erro simtatico, l %d, c %d:", it->token->linha,
							it->token->coluna);
					printf("valor lido - %s", simbolos[it->token->valorlido]);
					printf("\tvalor esperado - %s\n",
							simbolos[it->token->valoresperado]);
					this->volta = true;
					return;
				} else {
					printf("- %s", simbolos[it->token->valorlido]);
				}
				break;
			case BOLEANO:
				if (it->erro != SEM_ERROS) {
					printf("erro simtatico, l %d, c %d;", it->token->linha,
							it->token->coluna);
					this->volta = true;
					return;
				} else {
					printf("- %s", reservadas[it->token->valorlido]);
				}
				break;
			case ERRO:
				printf("Erro desconhecido");
				this->volta = true;
				return;
			default:
				printf("Situação inusitada, erro sintatico.");
				this->volta = true;
				return;
			}
		} else {
			naoTerminal(&(*it));
		}
	}
}
